//
//  NetworkClient.swift
//  DigitalMuseum
//
//  Created by zhongshan on 2024/2/20.
//

import Foundation
import Alamofire

protocol NetworkCancellable {
    var isCancelled: Bool { get }
    func cancel()
}

extension Alamofire.Request: NetworkCancellable {
    func cancel() {
        let _: Alamofire.Request = cancel()
    }
}

final class NetworkClient {
    
    static let shared = NetworkClient(session: Session.default)
    
    let session: Alamofire.Session
    
    private init(session: Alamofire.Session) {
        self.session = session
    }
    
    // MARK: - Data
    
    func requestDecoded<T: Decodable>(_ convertible: NetworkURLRequestConvertible,
                                      decodableType: T.Type,
                                      completionHandler: @escaping (_ result: Result<T, Error>) -> ()) -> NetworkCancellable {
        session.request(convertible).validate().responseDecodable(of: decodableType) { dataResponse in
            switch dataResponse.result {
            case .success(let success):
                completionHandler(.success(success))
            case .failure(let failure):
                completionHandler(.failure(failure))
            }
        }
    }
    
    func request(_ convertible: URLConvertible,
                 method: HTTPMethod = .get,
                 parameters: Parameters? = nil,
                 encoding: ParameterEncoding = URLEncoding.default,
                 headers: HTTPHeaders? = nil,
                 interceptor: RequestInterceptor? = nil,
                 requestModifier: Alamofire.Session.RequestModifier? = nil) -> Alamofire.DataRequest {
        var combineParams = NetworkServerConfig.commonParams
        if let parameters {
            combineParams.merge(parameters, uniquingKeysWith: { $1 })
        }
        return session.request(convertible, method: method, parameters: combineParams, encoding: encoding, headers: headers, interceptor: interceptor, requestModifier: requestModifier)
    }
    
    func request(_ convertible: URLRequestConvertible, interceptor: RequestInterceptor? = nil) -> Alamofire.DataRequest {
        session.request(convertible, interceptor: interceptor)
    }
    
    // MARK: - Download
    
    func download(_ convertible: URLConvertible,
                  method: HTTPMethod = .get,
                  parameters: Parameters? = nil,
                  encoding: ParameterEncoding = URLEncoding.default,
                  headers: HTTPHeaders? = nil,
                  interceptor: RequestInterceptor? = nil,
                  requestModifier: Alamofire.Session.RequestModifier? = nil,
                  to destination: DownloadRequest.Destination? = nil) -> Alamofire.DownloadRequest {
        session.download(convertible, method: method, parameters: parameters, encoding: encoding, headers: headers, interceptor: interceptor, requestModifier: requestModifier, to: destination)
    }
    
    
    // MARK: - Upload
    
    func upload(_ data: Data,
                to convertible: URLConvertible,
                method: HTTPMethod = .post,
                headers: HTTPHeaders? = nil,
                interceptor: RequestInterceptor? = nil,
                fileManager: FileManager = .default,
                requestModifier: Alamofire.Session.RequestModifier? = nil) -> Alamofire.UploadRequest {
        session.upload(data, to: convertible, method: method, headers: headers, interceptor: interceptor, fileManager: fileManager, requestModifier: requestModifier)
    }
    
    func upload(_ data: Data,
                with convertible: URLRequestConvertible,
                interceptor: RequestInterceptor? = nil,
                fileManager: FileManager = .default) -> Alamofire.UploadRequest {
        session.upload(data, with: convertible, interceptor: interceptor, fileManager: fileManager)
    }
    
    func upload(multipartFormData: @escaping (MultipartFormData) -> Void,
                to url: URLConvertible,
                usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold,
                method: HTTPMethod = .post,
                headers: HTTPHeaders? = nil,
                interceptor: RequestInterceptor? = nil,
                fileManager: FileManager = .default,
                requestModifier: Alamofire.Session.RequestModifier? = nil) -> Alamofire.UploadRequest {
        session.upload(multipartFormData: multipartFormData, to: url, usingThreshold: encodingMemoryThreshold, method: method, headers: headers, interceptor: interceptor, fileManager: fileManager, requestModifier: requestModifier)
    }
    
    func upload(multipartFormData: MultipartFormData,
                to url: URLConvertible,
                usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold,
                method: HTTPMethod = .post,
                headers: HTTPHeaders? = nil,
                interceptor: RequestInterceptor? = nil,
                fileManager: FileManager = .default,
                requestModifier: Alamofire.Session.RequestModifier? = nil) -> Alamofire.UploadRequest {
        session.upload(multipartFormData: multipartFormData, to: url, usingThreshold: encodingMemoryThreshold, method: method, headers: headers, interceptor: interceptor, fileManager: fileManager, requestModifier: requestModifier)
    }
    
    func upload(multipartFormData: @escaping (MultipartFormData) -> Void,
                with request: URLRequestConvertible,
                usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold,
                interceptor: RequestInterceptor? = nil,
                fileManager: FileManager = .default) -> Alamofire.UploadRequest {
        session.upload(multipartFormData: multipartFormData, with: request, usingThreshold: encodingMemoryThreshold, interceptor: interceptor, fileManager: fileManager)
    }
    
    func upload(multipartFormData: MultipartFormData,
                with request: URLRequestConvertible,
                usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold,
                interceptor: RequestInterceptor? = nil,
                fileManager: FileManager = .default) -> Alamofire.UploadRequest {
        session.upload(multipartFormData: multipartFormData, with: request, usingThreshold: encodingMemoryThreshold, interceptor: interceptor, fileManager: fileManager)
    }
}
